package springsecurityrabclogin.config;

import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
import springsecurityrabclogin.filter.CaptchaFilter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import springsecurityrabclogin.filter.TokenFilter;
import springsecurityrabclogin.handler.AppLogoutSuccessHandler;
import springsecurityrabclogin.handler.MyAccessDeniedHandler;
import springsecurityrabclogin.handler.MyAuthenticationFailureHandler;
import springsecurityrabclogin.handler.MyAuthenticationSuccessHandler;

import java.util.Arrays;
import java.util.Collections;

//@EnableMethodSecurity(prePostEnabled = true) //prePostEnabled默认为true 可不用配置
@EnableMethodSecurity
@Configuration //配置spring的容器，类似spring.xml文件一样
public class SecurityConfig {
    @Resource
    private MyAccessDeniedHandler myAccessDeniedHandler;
    @Resource
    private MyAuthenticationSuccessHandler myAuthenticationSuccessHandler;
    @Resource
    private MyAuthenticationFailureHandler myAuthenticationFailureHandler;
    @Resource
    private AppLogoutSuccessHandler appLogoutSuccessHandler;
    @Resource
    private TokenFilter tokenFilter;
    @Resource
    private CaptchaFilter captchaFilter;
    /**
     * <bean id="passwordEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"></bean>
     * @return
     */
    @Bean //配置一个spring的bean，bean的id就是方法名，bean的class就是方法的返回类型
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    @Bean
    public CorsConfigurationSource corsConfigurationSource() {
        return new UrlBasedCorsConfigurationSource() {
            @Override
            public CorsConfiguration getCorsConfiguration(HttpServletRequest request) {
                CorsConfiguration config = new CorsConfiguration();
                String origin = request.getHeader("Origin");
                if (origin != null) {
                    config.setAllowedOrigins(Collections.singletonList(origin)); // 动态设置允许的来源
                }
                config.setAllowedMethods(Arrays.asList("GET", "POST", "PUT", "DELETE","OPTIONS")); // 按需调整允许的方法
                config.setAllowedHeaders(Arrays.asList("*")); // 允许所有请求头
                config.setAllowCredentials(true); // 允许携带凭证
                return config;
            }
        };
    }
    //配置spring security框架的一些行为配置自己的登录页，不使用框架默认的配置页）
    //但是当配置了SecurityFilterChain这个Bean之后，Spring security框架的某些默认行为就丢失了（失效），此时需要加回来（捡回来）
    @Bean //安全过滤器链Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity httpSecurity) throws Exception {    //httpSecurity是方法参数注入bean
        //在spring security框架开发时，创建SpringFilterChain这个Bean，不是直接new DefaultSecurityChain
        return httpSecurity
                //配置自己的登录页
                .formLogin( (formLogin) ->{
                    //框架默认接受登录提交请求的地址是 /login ，但是默认行为丢失了（失效），此时需要加回来（捡回来）
                    formLogin.loginProcessingUrl("/user/login") //登录账号密码往哪个地址提交
                            .successHandler(myAuthenticationSuccessHandler) //登录成功后执行该handler
                            .failureHandler(myAuthenticationFailureHandler); //登录失败后执行该handler

                    //前后端分离时，在访问/user/login地址之前，对于后端应用来说，没有上一个地址（原地），默认跳转到项目根路径/
                })

                .logout( (logout) ->{
                    logout.logoutUrl("/user/logout") //退出请求提交到哪个地址
                            .logoutSuccessHandler(appLogoutSuccessHandler); //退出成功后执行该handler
                            //spring security 没有退出失败的处理

                 })
                //把所有接口都会进行登录状态检查的默认行为再加回来
                .authorizeHttpRequests( (authorizeHttpRequests) ->{
                    authorizeHttpRequests
//                            .requestMatchers("/toLogin","/common/captcha").permitAll() //特殊情况设置，permitAll允许不登录就可以访问
                            .anyRequest().authenticated(); //除了上面的特殊请求外，其他任何对后端接口的请求都需要认证（登录）后才可以访问
                })
                //验证码filter加在接收登录账号密码的filter之前
//                .addFilterBefore(captchaFilter, UsernamePasswordAuthenticationFilter.class)
                .csrf( (csrf) ->{
                    //禁用跨站请求伪造(csrf)，禁用之后不安全了，有csrf网络攻击的风险，后续加入jwt可以防御
                    csrf.disable();
                })

                .cors( (cors) ->{
                    //允许前端跨域访问
                    cors.configurationSource(corsConfigurationSource());
                })

                .sessionManagement( (sessionManagement) ->{
                    //session创建策略 （无session状态）
                    sessionManagement.sessionCreationPolicy(SessionCreationPolicy.STATELESS);
                })

                //在登录Filter之后加入我们token验证的Filter
//                .addFilterAfter(tokenFilter,UsernamePasswordAuthenticationFilter.class)
                .addFilterBefore(tokenFilter, LogoutFilter.class)

                .exceptionHandling( (exceptionHandling) ->{
                    exceptionHandling.accessDeniedHandler(myAccessDeniedHandler);//没有权限的时候，执行该handler
                })

                .build();
    }

    //前后端不分离： JSP（过时）、模版技术 Thymeleaf、FreeMarker、Velocity（几乎过时）
    //前后端分离：比较流行的，代表技术：Vue.js、React.js

}
